package com.m4f.city.business.oauth2.impl;

import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.SerializationUtils;
import org.springframework.security.oauth2.provider.AuthorizedClientAuthenticationToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.RandomValueOAuth2ProviderTokenServices;

import com.google.appengine.api.datastore.Blob;
import com.m4f.city.business.domain.InternalUser;
import com.m4f.city.business.domain.oauth2.OAuth2AccessTokenDS;
import com.m4f.city.business.domain.oauth2.OAuth2RefreshTokenDS;
import com.m4f.city.business.oauth2.ifc.OAuth2TokensService;

public class DataStoreOAuth2ProviderTokenServices extends RandomValueOAuth2ProviderTokenServices {
	private OAuth2TokensService tokensService;
		
	public DataStoreOAuth2ProviderTokenServices(OAuth2TokensService tokensService) {
		this.tokensService = tokensService;	
	}
	
	@Override
	protected OAuth2AccessToken readAccessToken(String token) {
		OAuth2AccessToken accessToken = null;

	    try {
	    	OAuth2AccessTokenDS oauthToken = this.tokensService.getAccessToken(token);
	    	if(oauthToken != null) {
	    		accessToken = SerializationUtils.deserialize(oauthToken.getAccessToken().getBytes());
	    	}
	    }
	    catch (Exception e) {
	    	e.printStackTrace();
	     /* if (LOG.isInfoEnabled()) {
	        LOG.info("Failed to find access token for token " + tokenValue);
	      }*/
	    }

	    return accessToken;
	}

	@Override
	protected OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
		OAuth2Authentication authentication = null;
		
	    try {
	    	OAuth2AccessTokenDS oauthToken = this.tokensService.getAccessToken(token.getValue());
	    	if(oauthToken != null) {
	    		authentication = SerializationUtils.deserialize(oauthToken.getAuthentication().getBytes());
	    	}
	    } catch (Exception e) { // TODO 
	    	e.printStackTrace();
	      /*if (LOG.isInfoEnabled()) {
	        LOG.info("Failed to find access token for token " + token);
	      }*/
	    }

	    return authentication;
	}

	@Override
	protected OAuth2Authentication readAuthentication(ExpiringOAuth2RefreshToken token) {
		OAuth2Authentication authentication = null;
		
	    try {
	    	OAuth2RefreshTokenDS oauthToken = this.tokensService.getRefreshToken(token.getValue());
	    	if(oauthToken != null) {
	    		authentication = SerializationUtils.deserialize(oauthToken.getAuthentication().getBytes());
	    	}
	    } catch (Exception e) { // TODO 
	    	e.printStackTrace();
	      /*if (LOG.isInfoEnabled()) {
	        LOG.info("Failed to find access token for token " + token);
	      }*/
	    }

	    return authentication;
	}

	@Override
	protected ExpiringOAuth2RefreshToken readRefreshToken(String tokenValue) {
		ExpiringOAuth2RefreshToken token = null;
		
		try {
	    	OAuth2RefreshTokenDS oauthToken = this.tokensService.getRefreshToken(tokenValue);
	    	if(oauthToken != null) {
	    		token = SerializationUtils.deserialize(oauthToken.getRefreshToken().getBytes());
	    	}
	    } catch (Exception e) { // TODO 
	    	e.printStackTrace();
	      /*if (LOG.isInfoEnabled()) {
	        LOG.info("Failed to find access token for token " + token);
	      }*/
	    }

	    return token;
	}

	@Override
	protected void removeAccessToken(String token) {
	    try {
	    	this.tokensService.removeAccessToken(token);
	    }
	    catch (Exception e) {
	    	e.printStackTrace();
	    }	
	}

	@Override
	protected void removeAccessTokenUsingRefreshToken(String token) {
		try {
	    	this.tokensService.removeAccessTokenByRefresToken(token);
	    }
	    catch (Exception e) {
	    	e.printStackTrace();
	    }	
	}

	@Override
	protected void removeRefreshToken(String token) {
		try {
	    	this.removeRefreshToken(token);
	    }
	    catch (Exception e) {
	    	e.printStackTrace();
	    }	
	}

	@Override
	protected void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication auth) {
		String refreshToken = null;
		InternalUser user = null;
	    AuthorizedClientAuthenticationToken authClient = null;
	    OAuth2AccessTokenDS accessTokenDS = null;
	    
	    // TODO this can change in a future, if we could change a way to authenticate app with other model
	    if(auth != null && auth.getClientAuthentication() != null && auth.getUserAuthentication().getPrincipal() instanceof InternalUser) {
	    	user = (InternalUser) auth.getUserAuthentication().getPrincipal();
	    	
	    }
	    if(auth != null && auth.getClientAuthentication() != null && auth.getClientAuthentication() instanceof AuthorizedClientAuthenticationToken) {
	    	authClient = (AuthorizedClientAuthenticationToken) auth.getClientAuthentication();
	    	
	    }
	    if (token.getRefreshToken() != null) {
	      refreshToken = token.getRefreshToken().getValue();
	    }
	    
	    try {
	    	//accessTokenDS = this.tokensService.getAccessToken(user.getId(), authClient.getClientId());
	    	this.tokensService.removeAccessToken(user.getId(), authClient.getClientId());
	    } catch(Exception e) {
	    	e.printStackTrace(); // TODO print log message to inform that is the first time that this user try to get an access token
	    }
	    if(accessTokenDS == null) {
	    	accessTokenDS = this.tokensService.createAccessToken();
	    }
	    
	    accessTokenDS.setAccessTokenValue(token.getValue());
	    accessTokenDS.setAccessToken(new Blob(SerializationUtils.serialize(token)));
	    accessTokenDS.setRefreshTokenValue(refreshToken);
	    accessTokenDS.setRefreshToken(new Blob(SerializationUtils.serialize(token.getRefreshToken())));
	    accessTokenDS.setAuthentication(new Blob(SerializationUtils.serialize(auth)));
	    
	    accessTokenDS.setUserId(user.getId());
	    accessTokenDS.setClientId(authClient.getClientId());
	 
		try {
			this.tokensService.save(accessTokenDS);
		} catch(Exception e) { // TODO handle this exception
			e.printStackTrace();
		}
	}

	@Override
	protected void storeRefreshToken(ExpiringOAuth2RefreshToken token, OAuth2Authentication auth) {
		OAuth2RefreshTokenDS refreshTokenDS = null;
		InternalUser user = null;
		AuthorizedClientAuthenticationToken authClient = null;
		
		if(auth != null && auth.getClientAuthentication() != null && auth.getUserAuthentication().getPrincipal() instanceof InternalUser) {
	    	user = (InternalUser) auth.getUserAuthentication().getPrincipal();
	    }
		if(auth != null && auth.getClientAuthentication() != null && auth.getClientAuthentication() instanceof AuthorizedClientAuthenticationToken) {
	    	authClient = (AuthorizedClientAuthenticationToken) auth.getClientAuthentication();
	    }
			
		try {
			//refreshTokenDS = this.tokensService.getRefreshToken(user.getId(), authClient.getClientId());
			this.tokensService.removeRefreshToken(user.getId(), authClient.getClientId());
		} catch(Exception e) {
			e.printStackTrace(); // TODO print log message to inform that is the first time that this user try to get an access token
		}
		if(refreshTokenDS == null) {
			refreshTokenDS = this.tokensService.createRefreshToken();
		}
		
		refreshTokenDS.setRefreshTokenValue(token.getValue());
		refreshTokenDS.setRefreshToken(new Blob(SerializationUtils.serialize(token)));
		refreshTokenDS.setAuthentication(new Blob(SerializationUtils.serialize(auth)));
		
		refreshTokenDS.setUserId(user.getId());
		refreshTokenDS.setClientId(authClient.getClientId());
	    
		try {
			this.tokensService.save(refreshTokenDS);
		} catch(Exception e) { // TODO handle this exception
			e.printStackTrace();
		}
	}
}
